﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using Reflector;
using Reflector.CodeModel;
using Reflector.Graph.Graphs;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading;
using Reflector.Graph;
using System.IO;
using System.Reflection.Emit;
using System;

namespace MethodSeqGenerator
{
    /*
     * A class for searching method declarations that includes pieces of
     * required code. This class also invokes analysis phase to gather method sequences
     */ 
    class MCodeSearch
    {
        IServiceProvider serviceProvider = null;
        SeqGenerator seqGenObj = null;
        public void SearchInTheCode(List<String> searchStringList, IServiceProvider serviceProvider, SeqGenerator seqGenObj)
        {
            this.seqGenObj = seqGenObj;
            
            //Get the dlls from the directory
            DirectoryInfo directoryInfo = new DirectoryInfo(SeqGenerator.DLL_LOCATION_DIR);
            foreach (FileInfo fileInfo in directoryInfo.GetFiles())
            {
                if (SeqGenerator.ignoreDLLSet.Contains(fileInfo.Name))
                {
                    continue;
                }

                SearchInDLL(searchStringList, SeqGenerator.DLL_LOCATION_DIR + "\\" + fileInfo.Name, serviceProvider);                
            }            
        }

        public void SearchInDLL(List<String> searchStringList, String dllName, IServiceProvider serviceProvider)
        {
            IAssemblyManager assemblyManager = (IAssemblyManager)serviceProvider.GetService(typeof(IAssemblyManager));
            this.serviceProvider = serviceProvider;
            IAssembly assembly = assemblyManager.LoadFile(dllName);
            SeqGenerator.currentDLL = dllName;
            SeqGenerator.swLogging.WriteLine("Analyzing DLL:" + dllName);
            //SeqGenerator.swLogging.Flush();

            if (assembly != null)
            {
                foreach (IModule module in ((IAssembly)assembly).Modules)
                {
                    foreach (IType t in ((IModule)module).Types)
                    {
                        ITypeDeclaration td = (ITypeDeclaration)t;
                        SeqGenerator.currentTypeDeclarationName = td.Name;
                        foreach (IMethodDeclaration m in ((ITypeDeclaration)td).Methods)
                        {
                            //if (SearchInMethodBinary(searchStringList, m))
                            if (SearchInMethod(searchStringList, m))
                            {
                                SeqGenerator.currentMethodName = m.Name;
                                seqGenObj.AnalyzeMethodDeclaration(m, searchStringList);
                            }
                        }
                    }
                }
            }
            //assemblyManager.Unload(assembly);
        }

        /*
         * A little more efficient search in the binary code rather than decompiling the binaries into sources
         */
        public bool SearchInMethodBinary(List<String> searchStringList, IMethodDeclaration md)
        {
            IMethodBody imbObj = (IMethodBody) md.Body;
            if (imbObj == null)
                 return false;
            
            IInstructionCollection iicObj = imbObj.Instructions;
            if (iicObj != null && iicObj.Count > 0)
            {
                foreach (IInstruction iisObj in iicObj)
                {
                    if (iisObj.Code == OpCodes.Newobj.Value)
                    {
                        String insValue = iisObj.Value.ToString();
                        foreach (String searchStr in searchStringList)
                        {
                            int lastIndex = searchStr.LastIndexOf(".");
                            String modifiedStr = searchStr.Substring(lastIndex + 1, searchStr.Length - lastIndex - 1);
                            if (insValue.Contains(modifiedStr))
                                return true;                            
                        }
                    }
                }
            }

            return false;
        }
        
        /*
         * An inefficient search where code is decompiled into source and a 
         * reglular expression matching is done on the code
         */
        public bool SearchInMethod(List<String> searchStringList, IMethodDeclaration method)
        {
            string source = GetSourceCode(method);
            if (source != null)
            {
                try
                {
                    MatchCollection mc = null;                  
                    //Search whether the method declaration includes atleast one search string
                    foreach (String searchStr in searchStringList)
                    {
                        int lastIndex = searchStr.LastIndexOf(".");
                        String modifiedStr = searchStr.Substring(lastIndex + 1, searchStr.Length - lastIndex - 1);
                        Regex r = new Regex(modifiedStr);
                        mc = r.Matches(source);
                        if (mc.Count > 0)
                        {
                            return true;
                        }
                    }                   
                }
                catch (Exception e)
                {
                    string sEx = e.ToString();
                }               
            }

            return false;
        }

        public string GetSourceCode(object method)
        {
            ILanguageManager languageManager = (ILanguageManager)serviceProvider.GetService(typeof(ILanguageManager));
            ITranslatorManager translatorManager = (ITranslatorManager)serviceProvider.GetService(typeof(ITranslatorManager));
            IVisibilityConfiguration visibilityConfiguration = (IVisibilityConfiguration)serviceProvider.GetService(typeof(IVisibilityConfiguration));
            TextFormatter formatter = new TextFormatter();
            ILanguage language = languageManager.ActiveLanguage;

            LanguageWriterConfiguration configuration = new LanguageWriterConfiguration();
            if ((language != null) && (formatter != null))
            {
                ILanguageWriter writer = language.GetWriter(formatter, configuration);
                if (writer != null)
                {
                    try
                    {
                        IMethodDeclaration md = null;
                        if (language.Translate)
                        {
                            ITranslator translator = translatorManager.CreateDisassembler(null, null);
                            if (translator != null)
                                md = translator.TranslateMethodDeclaration((IMethodDeclaration)method);                            
                        }
                        else
                            md = (IMethodDeclaration)method;
                        
                        writer.WriteMethodDeclaration(md);
                        return formatter.ToString();
                    }
                    catch (Exception ex)
                    {                        
                    }
                }
            }
            return null;
        }

        private class LanguageWriterConfiguration : ILanguageWriterConfiguration
        {
            private IVisibilityConfiguration visibility;
            public IVisibilityConfiguration Visibility
            {
                get { return this.visibility; }
                set { this.visibility = value; }
            }
            public string this[string name]
            {
                get
                {
                    switch (name)
                    {
                        case "ShowMethodDeclarationBody":
                        case "ShowCustomAttributes":
                            return "true";
                        default:
                            return "false";
                    }
                }
            }
        }
    }    
}
